package com.fashion.service;

import com.fashion.dao.NewsColumnItemDao;
import com.fashion.dao.NewsDao;
import com.fashion.exceptions.NewsNotFoundException;
import com.fashion.model.*;
import com.fashion.utils.NewsTemplate;
import com.fashion.utils.NewsTemplateSorter;
import com.fashion.utils.StringUtils;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.apache.struts2.ServletActionContext;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URL;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;

import static java.util.Collections.sort;

/**
 * User: juventus
 * Date: 11-5-30
 */
@Service("newsService")
public class NewsServiceImpl implements NewsService {
    private NewsDao newsDao;
    private NewsColumnItemDao newsColumnItemDao;
    private NewsTemplateSorter newsTemplateSorter;
    private static final int Suggest_News_Title_Length = 50;
    private static final int Suggest_News_Introduction_Length = 60;
    private static final int Hotest_News_Title_Length = 18;
    private static final int Hotest_News_Introduction_Length = 48;
    private static final int Tag_News_Title_Length = 35;
    private static final int Column_News_Title_Length = 35;
    private static final Logger logger = Logger.getLogger(NewsServiceImpl.class);

    @Override
    public News getNewsById(String id) throws NewsNotFoundException {
        return newsDao.getEntityById(News.class, id);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)
    public void saveOrUpdate(Object object) {
        newsDao.saveOrUpdate(object);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)
    public void deleteEntityById(String id) {
        newsDao.deleteById(id, News.class);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)
    public void deleteEntity(Object object) {
        newsDao.delete(object);
    }

    @Override
    public List<News> searchNews(Administrator author, String title, String newsIndexTitle, String newsColumnId, int inIndex, int indexLevel, int pageNumber, int pageSize) {
        /*StringBuilder basicHql = new StringBuilder("select new News(n.id,n.title, n.indexTitle, n.keywords,n.createDate,n.indexLevel,n.inIndex) " +
                "from News n, NewsColumnItem i, NewsColumn c  where i.newsId=n.id and i.columnId=c.id ");*/
        StringBuilder basicHql = new StringBuilder("select new News(n.id,n.title, n.indexTitle, n.keywords,n.publishDate,n.createDate,n.indexLevel,n.inIndex,n.published) " +
                "from News n, NewsColumnItem i, NewsColumn c  where i.newsId=n.id and i.columnId=c.id ");
        Map<String, Object> parameters = new HashMap<String, Object>();
        if (author.getRole() != 1) {
            basicHql.append(" and n.creatorId = :creatorId");
            parameters.put("creatorId", author.getId());
        }

        if (inIndex > -1) {
            basicHql.append(" and n.inIndex = :inIndex");
            parameters.put("inIndex", inIndex);
        }

        if (indexLevel > -1) {
            basicHql.append(" and n.indexLevel = :indexLevel");
            parameters.put("indexLevel", indexLevel);
        }

        prepareParametersWithIndexTitle(title, newsIndexTitle, newsColumnId, basicHql, parameters);
        basicHql.append(" group by n order by n.createDate desc");

        return newsDao.findItemsWithPage(basicHql.toString(), parameters, pageNumber, pageSize);
    }


    @Override
    public int countNews(Administrator author, String title, String indexTitle, String newsColumnId, int inIndex, int indexLevel) {
        StringBuilder basicHql = new StringBuilder("select count(distinct n.id) " +
                "from News n, NewsColumnItem i, NewsColumn c  where i.newsId=n.id and i.columnId=c.id ");
        Map<String, Object> parameters = new HashMap<String, Object>();

        if (author.getRole() == 0) {
            basicHql.append(" and n.creatorId = :creatorId");
            parameters.put("creatorId", author.getId());
        }

        if (inIndex > -1) {
            basicHql.append(" and n.inIndex = :inIndex");
            parameters.put("inIndex", inIndex);
        }

        if (indexLevel > -1) {
            basicHql.append(" and n.indexLevel = :indexLevel");
            parameters.put("indexLevel", indexLevel);
        }

        prepareParametersWithIndexTitle(title, indexTitle, newsColumnId, basicHql, parameters);

        return newsDao.countItems(basicHql.toString(), parameters);
    }

    @Override
    public void handleColumnsForNews(News news, List<String> columnIds) {
        List<NewsColumnItem> oldColumnItems = newsColumnItemDao.find("from NewsColumnItem i where i.newsId = ? ", news.getId());
        newsColumnItemDao.deleteAll(oldColumnItems);

        for (String columnId : columnIds) {
            //NewsColumn column = newsColumnItemDao.getEntityById(NewsColumn.class, (columnId.trim()));
            NewsColumnItem newsColumnItem = new NewsColumnItem();
            newsColumnItem.setId(StringUtils.createGuid());
            newsColumnItem.setNewsId(news.getId());
            newsColumnItem.setColumnId(columnId.trim());

            newsColumnItemDao.saveOrUpdate(newsColumnItem);
        }
    }

    @Override
    public List<News> searchAboutNews(String newsId, int pageNumber, int pageSize) {
        String hql = "select new News(n.id,n.title,n.keywords,n.createDate,n.briefImage,n.briefImageTitle,n.introduction,n.briefImageVertical,n.author, n.source) from News n, AboutNews i " +
                " where i.targetNews.id = n.id" +
                " and i.sourceNews.id=:id order by n.publishDate";
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("id", newsId);

        return newsDao.findItemsWithPage(hql, parameters, pageNumber, pageSize);
    }

    @Override
    public int countAboutNews(String newsId) {
        String hql = "select count(n.id) from News n, AboutNews i where i.targetNews.id =n.id " +
                "and i.sourceNews.id=:id";

        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("id", newsId);

        return newsDao.countItems(hql, parameters);
    }

    @Override
    public int countNonAboutNews(String id, String newsTitle, String newsColumnId) {
        StringBuilder basicHql = new StringBuilder("select count(n.id) " +
                " from News n, NewsColumnItem i where i.newsId=n.id and n.id!=:id0" +
                " and n.id not in (select ne.id from News ne, AboutNews an  where an.targetNews.id=ne.id and an.sourceNews.id=:id)");
        Map<String, Object> parameters = new HashMap<String, Object>();
        prepareParameters(newsTitle, newsColumnId, basicHql, parameters);
        parameters.put("id", id);
        parameters.put("id0", id);

        return newsDao.countItems(basicHql.toString(), parameters);
    }

    @Override
    public List<News> searchNonAboutNews(String newsId, String newsTitle, String newsColumnId, int pageNumber, int pageSize) {
        StringBuilder basicHql = new StringBuilder("select new News(n.id,n.title,n.keywords,n.createDate) " +
                " from News n, NewsColumnItem i where i.newsId=n.id and n.id!=:id0" +
                " and n.id not in (select ne.id from News ne, AboutNews an  where an.targetNews.id=ne.id and an.sourceNews.id=:id)");

        Map<String, Object> parameters = new HashMap<String, Object>();
        prepareParameters(newsTitle, newsColumnId, basicHql, parameters);
        parameters.put("id", newsId);
        parameters.put("id0", newsId);

        return newsDao.findItemsWithPage(basicHql.toString(), parameters, pageNumber, pageSize);
    }

    @Override
    public void addAboutNews(String sourceNewsId, String targetNewsId) {
        News sourceNews = newsDao.getEntityById(News.class, sourceNewsId);
        News targetNews = newsDao.getEntityById(News.class, targetNewsId);

        AboutNews aboutNews = new AboutNews();
        aboutNews.setId(StringUtils.createGuid());
        aboutNews.setSourceNews(sourceNews);
        aboutNews.setTargetNews(targetNews);

        newsDao.saveOrUpdate(aboutNews);
    }

    @Override
    public void deleteAboutNews(String sourceNewsId, String targetNewsId) {
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("sourceNewsId", sourceNewsId);
        parameters.put("targetNewsId", targetNewsId);

        List<AboutNews> aboutNewsList = newsDao.findItems("from AboutNews a where a.sourceNews.id=:sourceNewsId and a.targetNews.id=:targetNewsId",
                parameters);

        newsDao.deleteAll(aboutNewsList);
    }

    @Override
    public List<News> searchSuggestNews(String newsTitle, String newsColumnId, int page, int rows) {
        StringBuilder basicHql = new StringBuilder("select new News(n.id,n.indexTitle, n.title,n.keywords,n.createDate,n.briefImage,n.briefImageTitle,n.introduction,n.briefImageVertical,n.author, n.source) " +
                "from News n where n.suggest = :suggest  and n.published=:published ");

        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("suggest", true);
        parameters.put("published", true);

        prepareParameters(newsTitle, newsColumnId, basicHql, parameters);

        List<News> suggestNewsList = newsDao.findItemsWithPage(basicHql.toString(), parameters, page, rows);
        /* for (News suggestNews : suggestNewsList) {
            formatNews(suggestNews, Suggest_News_Title_Length, Suggest_News_Introduction_Length);
        }*/

        return suggestNewsList;
    }

    @Override
    public int countSuggestNews(String newsTitle, String newsColumnId) {
        StringBuilder basicHql = new StringBuilder("select count(n.id) " +
                "from News n, NewsColumnItem i  where i.newsId=n.id and n.suggest=:suggest ");

        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("suggest", true);
        prepareParameters(newsTitle, newsColumnId, basicHql, parameters);

        return newsDao.countItems(basicHql.toString(), parameters);
    }

    @Override
    public List<News> searchNonSuggestNews(String newsTitle, String newsColumnId, int page, int rows) {
        StringBuilder basicHql = new StringBuilder("select new News(n.id,n.title,n.keywords,n.createDate) " +
                "from News n, NewsColumnItem i where i.newsId=n.id and n.suggest=:suggest");

        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("suggest", false);
        prepareParameters(newsTitle, newsColumnId, basicHql, parameters);

        return newsDao.findItemsWithPage(basicHql.toString(), parameters, page, rows);
    }

    @Override
    public int countNonSuggestNews(String newsTitle, String newsColumnId) {
        StringBuilder basicHql = new StringBuilder("select count(n.id) " +
                " from News n, NewsColumnItem i where i.newsId=n.id and n.suggest=:suggest ");

        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("suggest", false);
        prepareParameters(newsTitle, newsColumnId, basicHql, parameters);

        return newsDao.countItems(basicHql.toString(), parameters);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)
    public void addSuggestNews(String targetNewsId) {
        News news = getNewsById(targetNewsId);
        news.setSuggest(true);

        newsDao.saveOrUpdate(news);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)
    public void deleteSuggestNews(String[] newsIds) {
        String hql = "update News n set n.suggest=false where n.id in (:ids)";
        newsDao.bulkUpdate(hql, newsIds);
    }

    @Override
    public void saveThumbNail(File file, String folder, String saveName) throws IOException {
        File saveFolder = new File(folder);
        if (!saveFolder.exists()) {
            saveFolder.mkdirs();
        }

        FileUtils.copyFile(file, new File(folder, saveName));
    }

    @Override
    public boolean checkVerticalImage(File file) throws IOException {
        FileInputStream fileInputStream = new FileInputStream(file);
        BufferedImage buff = ImageIO.read(fileInputStream);

        return buff.getHeight() > buff.getWidth();
    }

    @Override
    public List<News> searchTagNews(String keyword, int page, int rows) {
        String hql = "select new News(n.id,n.title,n.keywords,n.createDate,n.briefImage,n.briefImageTitle,n.introduction,n.briefImageVertical,n.author, n.source) " +
                " from News n where n.keywords like :keywords and n.published=:published order by n.publishDate desc";
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("keywords","%" + keyword + "%");
        parameters.put("published", true);

        List<News> tagNewsList = newsDao.findItemsWithPage(hql, parameters, page, rows);
        /* for (News tagNews : tagNewsList) {
            formatNews(tagNews, Tag_News_Title_Length, 200);
        }*/

        return tagNewsList;
    }

    @Override
    public List<News> searchTagNews(List<String> keywords, int page, int rows) {
        Map<String, Object> parameters = new HashMap<String, Object>();
        StringBuffer hql = new StringBuffer("from News t where ");
        for (int i = 0; i < keywords.size(); i++) {
            if (i == 0)
                hql.append(" t.keywords like :keyword").append(i).append(" ");
            else
                hql.append(" OR t.keywords like :keyword").append(i).append(" ");
            parameters.put("keyword" + i, "%" + keywords.get(i) + "%");
        }
        //    String hql = "from News t " + hql + " order by t.createDate desc";
        hql.append(" order by t.publishDate desc");

        return newsDao.findItemsWithPage(hql.toString(), parameters, page, rows);
    }

    @Override
    public int countTagNews(String keyword) {
        String hql = "select count(t.id) from News t where t.keywords like :keywords and t.published=:published";
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("keywords","%" + keyword + "%");
        parameters.put("published", true);

        return newsDao.countItems(hql, parameters);
    }

    @Override
    public List<News> searchNewsByColumn(String newsColumnName, int pageNumber, int pageSize) {
        StringBuilder basicHql = new StringBuilder("select new News(n.id,n.title,n.keywords,n.createDate,n.briefImage,n.briefImageTitle,n.introduction,n.briefImageVertical,n.author, n.source) "
                + " from News n, NewsColumnItem i,NewsColumn c where i.newsId=n.id and i.columnId=c.id and "
                + " c.name=:columnName order by n.publishDate desc");

        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("columnName", newsColumnName);

        List<News> columnNewsList = newsDao.findItemsWithPage(basicHql.toString(), parameters, pageNumber, pageSize);
        /*  for (News columnNews : columnNewsList) {
            formatNews(columnNews, Column_News_Title_Length, 200);
        }*/

        return columnNewsList;
    }

    @Override
    public int countNewsByColumn(String columnName) {
        StringBuilder basicHql = new StringBuilder("select count(n.id) "
                + " from News n, NewsColumnItem i,NewsColumn c where i.newsId=n.id and i.columnId=c.id and "
                + " c.name=:columnName order by n.publishDate desc");

        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("columnName", columnName);

        return newsDao.countItems(basicHql.toString(), parameters);
    }

    @Override
    public List<News> searchNewsByColumnId(String columnId, int pageNumber, int pageSize) {
        StringBuilder basicHql = new StringBuilder("select new News(n.id,n.title,n.keywords,n.createDate,n.briefImage,n.briefImageTitle,n.introduction,n.briefImageVertical,n.author, n.source) "
                + " from News n, NewsColumnItem i,NewsColumn c where i.newsId=n.id and i.columnId=c.id and "
                + " c.id=:columnId order by n.publishDate desc");

        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("columnId", columnId);

        return newsDao.findItemsWithPage(basicHql.toString(), parameters, pageNumber, pageSize);
    }

    @Override
    public List<News> searchNewsByColumns(List<NewsColumn> columns, int pageNumber, int pageSize) {
        StringBuilder basicHql = new StringBuilder("select new News(n.id,n.title,n.keywords,n.createDate,n.briefImage,n.briefImageTitle,n.introduction,n.briefImageVertical,n.author, n.source) "
                + " from News n, NewsColumnItem i,NewsColumn c where i.newsId=n.id and i.columnId=c.id and "
                + " c.id in (:ids)  and n.published=:published  order by n.publishDate desc");

        Map<String, Object> parameters = new HashMap<String, Object>();
        List<String> ids = new ArrayList<String>();
        for (NewsColumn column : columns) {
            ids.add(column.getId());
        }
        parameters.put("ids", ids);
        parameters.put("published", true);

        List<News> columnNewsList = newsDao.findItemsWithPage(basicHql.toString(), parameters, pageNumber, pageSize);
        /*  for (News columnNews : columnNewsList) {
            formatNews(columnNews, Column_News_Title_Length, 200);
        }*/

        return columnNewsList;
    }

    @Override
    public int countNewsByColumnId(String columnId) {
        StringBuilder basicHql = new StringBuilder("select count(n.id) "
                + " from News n, NewsColumnItem i,NewsColumn c where i.newsId=n.id and i.columnId=c.id and "
                + " c.id=:columnId   and n.published=:published ");

        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("columnId", columnId);
        parameters.put("published", true);

        return newsDao.countItems(basicHql.toString(), parameters);
    }

    @Override
    public int countNewsByColumns(List<NewsColumn> columns) {
        StringBuilder basicHql = new StringBuilder("select count(n.id) "
                + " from News n, NewsColumnItem i,NewsColumn c where i.newsId=n.id and i.columnId=c.id and "
                + " c.id in (:ids)  and n.published=:published ");

        Map<String, Object> parameters = new HashMap<String, Object>();
        List<String> ids = new ArrayList<String>();
        for (NewsColumn column : columns) {
            ids.add(column.getId());
        }

        parameters.put("ids", ids);
        parameters.put("published", true);

        return newsDao.countItems(basicHql.toString(), parameters);
    }

    @Override
    public List<News> searchTopTenNews(String newsColumnName, int pageNumber, int pageSize) {
        String basicHql = "select new News(n.id,n.title,n.keywords,n.createDate,n.briefImage,n.briefImageTitle,n.introduction,n.briefImageVertical,n.author, n.source) " +
                "from News n order by n.viewTimes desc ";

        Map<String, Object> parameters = new HashMap<String, Object>();

        return newsDao.findItemsWithPage(basicHql, parameters, pageNumber, pageSize);
    }

    @Override
    public List<News> searchNewsByColumnAndTemplateList(String newsColumnName, List<NewsTemplate> newsTemplates, int inIndex) {
        sort(newsTemplates, newsTemplateSorter);
        List<News> matchedNews = new ArrayList<News>();

        for (NewsTemplate newsTemplate : newsTemplates) {
            List<News> newsList = searchNewsByColumnAndTemplate(newsColumnName, newsTemplate, inIndex);
            formatNewsListByIndexLevel(newsList, newsTemplate.getLevel());
            matchedNews.addAll(newsList);
        }

        return matchedNews;
    }

    public List<News> searchNewsByColumnListAndTemplate(List<NewsColumn> columns, NewsTemplate newsTemplate, int inIndex) {
        StringBuilder basicHql = new StringBuilder("select new News(n.id, n.title, n.indexTitle, n.keywords, n.publishDate,n.briefImage,n.briefImageTitle,n.introduction,n.briefImageVertical,n.inIndex,n.indexLevel) "
                + "from News n, NewsColumnItem i,NewsColumn c where i.newsId=n.id and i.columnId=c.id and "
                + "c.id in (:ids) and n.indexLevel=:indexLevel and n.inIndex>=:inIndex and n.published=:published order by n.createDate desc");

        Map<String, Object> parameters = new HashMap<String, Object>();
        List<String> ids = new ArrayList<String>();
        for (NewsColumn column : columns) {
            ids.add(column.getId());
        }
        parameters.put("ids", ids);
        parameters.put("indexLevel", newsTemplate.getLevel());
        parameters.put("inIndex", inIndex);
        parameters.put("published", true);

        return newsDao.findItemsWithPage(basicHql.toString(), parameters, 1, newsTemplate.getCount());
    }

    @Override
    public List<News> searchNewsByColumnListAndTemplateList(List<NewsColumn> columns, List<NewsTemplate> newsTemplates, int inIndex) {
        List<News> matchedNews = new ArrayList<News>();

        for (NewsTemplate newsTemplate : newsTemplates) {
            List<News> newsList = searchNewsByColumnListAndTemplate(columns, newsTemplate, inIndex);
            matchedNews.addAll(newsList);
        }

        return matchedNews;
    }

    @Override
    public List<News> searchNewsByTemplateList(List<NewsTemplate> newsTemplates, int inIndex) {
        sort(newsTemplates, newsTemplateSorter);
        List<News> matchedNews = new ArrayList<News>();

        for (NewsTemplate newsTemplate : newsTemplates) {
            List<News> newsList = searchNewsTemplate(newsTemplate, inIndex);
            matchedNews.addAll(newsList);
        }

        return matchedNews;
    }

    @Override
    public List<NewsColumnItem> findColumnItemsByNewsId(String id) {
        String hql = "from NewsColumnItem i where i.newsId=:id";
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("id", id);

        return newsDao.findItems(hql, parameters);
    }

    @Override
    public void bulkDeleteNewsColumnItems(List<String> itemIds) {
        if (itemIds.size() == 0) {
            return;
        }

        String hql = "delete NewsColumnItem t where t.id in (:ids)";
        newsDao.bulkDelete(hql, itemIds.toArray(new String[itemIds.size()]));
    }

    @Override
    public List<News> searchMostViewedNewsByColumn(String newsColumnId, int pageNumber, int pageSize) {
        String hql = "select new News(n.id,n.title,n.keywords,n.createDate,n.briefImage,n.briefImageTitle,n.introduction,n.briefImageVertical,n.author, n.source) "
                + "from News n, NewsColumnItem i,NewsColumn c where i.newsId=n.id and i.columnId=c.id and "
                + "c.id=:newsColumnId and n.published=:published order by n.viewTimes, n.createDate desc";
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("newsColumnId", newsColumnId);
        parameters.put("published", true);

        List<News> mostViewedNewsList = newsDao.findItemsWithPage(hql, parameters, pageNumber, pageSize);

        /* for (News news : mostViewedNewsList) {
            formatNews(news, Hotest_News_Title_Length, Hotest_News_Introduction_Length);
        }*/

        return mostViewedNewsList;
    }

    @Override
    public List<News> searchMostViewedNews(int pageNumber, int pageSize) {
        String hql = "select new News(n.id,n.title,n.keywords,n.createDate,n.briefImage,n.briefImageTitle,n.introduction,n.briefImageVertical,n.author, n.source) "
                + "from News n where n.published=:published  order by n.viewTimes desc, n.createDate desc";
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("published", true);

        List<News> mostViewedNewsList = newsDao.findItemsWithPage(hql, parameters, pageNumber, pageSize);
/*
        for (News news : mostViewedNewsList) {
            formatNews(news, Hotest_News_Title_Length, Hotest_News_Introduction_Length);
        }*/

        return mostViewedNewsList;
    }

    @Override
    public void formatNewsTitle(List<News> newsList, int maxTitleLength) {
        for (News news : newsList) {
            formatNews(news, maxTitleLength, maxTitleLength);
        }
    }

    @Override
    public List<News> searchAboutNewsByKeywordsList(List<String> rawKeywords, String id) {
        List<News> allAboutNewsList = new ArrayList<News>();

        int keywordsSearched = 0;
        Map<String, News> selectedNewsMap = new HashMap<String, News>();
        for (String rawKeyword : rawKeywords) {
            if (keywordsSearched > 1) {
                break;
            }

            List<News> aboutNewsList = searchAboutNewsByKeywords(rawKeyword);
            for (News news : aboutNewsList) {
                String newsId = news.getId();
                if(selectedNewsMap.containsKey(newsId) || newsId.equals(id)){
                    continue;
                }

                selectedNewsMap.put(newsId, news);
                if (selectedNewsMap.size() > 7) {
                    break;
                }
            }

            if (selectedNewsMap.size() > 7) {
                break;
            }
        }

        allAboutNewsList.addAll(selectedNewsMap.values());
        return allAboutNewsList;
    }

    @Override
    public List<News> searchDraftNews(String newsTitle, String newsIndexTitle, String newsColumn, int inIndex, int indexLevel, int page, int pageSize) {
        StringBuilder basicHql = new StringBuilder("select new News(n.id,n.title, n.indexTitle, n.keywords,n.publishDate,n.indexLevel,n.inIndex,n.published) " +
                "from News n, NewsColumnItem i, NewsColumn c  where i.newsId=n.id and i.columnId=c.id and n.published=:published");
        Map<String, Object> parameters = new HashMap<String, Object>();

        if (inIndex > -1) {
            basicHql.append(" and n.inIndex = :inIndex");
            parameters.put("inIndex", inIndex);
        }

        if (indexLevel > -1) {
            basicHql.append(" and n.indexLevel = :indexLevel");
            parameters.put("indexLevel", indexLevel);
        }

        parameters.put("published", false);

        prepareParametersWithIndexTitle(newsTitle, newsIndexTitle, newsColumn, basicHql, parameters);
        basicHql.append(" group by n order by n.createDate desc");

        return newsDao.findItemsWithPage(basicHql.toString(), parameters, page, pageSize);
    }

    @Override
    public int countDraftNews(String newsTitle, String newsIndexTitle, String newsColumn, int inIndex, int indexLevel) {
        StringBuilder basicHql = new StringBuilder("select count(distinct n.id) " +
                "from News n, NewsColumnItem i, NewsColumn c  where i.newsId=n.id and i.columnId=c.id and n.published=:published");
        Map<String, Object> parameters = new HashMap<String, Object>();

        if (inIndex > -1) {
            basicHql.append(" and n.inIndex = :inIndex");
            parameters.put("inIndex", inIndex);
        }

        if (indexLevel > -1) {
            basicHql.append(" and n.indexLevel = :indexLevel");
            parameters.put("indexLevel", indexLevel);
        }

        parameters.put("published", false);
        prepareParametersWithIndexTitle(newsTitle, newsIndexTitle, newsColumn, basicHql, parameters);

        return newsDao.countItems(basicHql.toString(), parameters);
    }

    @Override
    public void saveHtmlNews(String urlString, String urlPrefix, String basePersistenceFolder) throws Exception {
        String fileName = generateHtmlFileName(urlString,basePersistenceFolder);
        deleteOldHtml(fileName);

        saveOnePage(urlString, fileName);

        Document document = Jsoup.parse(new File(fileName), "UTF-8");
        Elements nextElements = document.select("div.paging-box a.next");
        if (nextElements.size() > 0) {
            saveHtmlNews(urlPrefix + nextElements.first().attr("href"), urlPrefix, basePersistenceFolder);
        }
    }

    @Override
    public List<News> findAllNews() {
        StringBuilder basicHql = new StringBuilder("select new News(n.id) " +
                "from News n, NewsColumnItem i, NewsColumn c  where i.newsId=n.id and i.columnId=c.id and n.published=:published");
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("published", true);
        basicHql.append(" group by n order by n.createDate desc");

        return newsDao.findItems(basicHql.toString(), parameters);
    }

    @Override
    public List<News> searchNewsByMonths(List<String> months) {
        if(null == months || months.size() == 0){
             return null;
        }

        StringBuilder basicHql = new StringBuilder("select new News(n.id) " +
                "from News n, NewsColumnItem i, NewsColumn c  where i.newsId=n.id and i.columnId=c.id and n.published=:published and(");
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("published", true);
        buildIdQuery(months, basicHql, parameters);

        basicHql.append(") group by n order by n.createDate desc");

        return newsDao.findItems(basicHql.toString(), parameters);
    }

    protected void buildIdQuery(List<String> months, StringBuilder basicHql, Map<String, Object> parameters) {
        for (int i = 0; i < months.size(); i++) {
            String month = months.get(i);

            if(i > 0){
                basicHql.append(" or ");
            }

            basicHql.append(" n.id like :id" + i);
            parameters.put("id" + i, month + "%");
        }
    }

    protected void saveOnePage(String urlString, String fileName) throws IOException {
        //just to open the url since htmlfilter will save html file
        Jsoup.parse(new URL(urlString), 30 * 1000);

       /* AtomicReference<InputStream> is = new AtomicReference<InputStream>();
        URL url = new URL(urlString);
        byte bytes[] = new byte[1024 * 1000];
        int index = 0;
        is.set(url.openStream());
        int count = is.get().read(bytes, index, 1024 * 100);

        while (count != -1) {
            index += count;
            count = is.get().read(bytes, index, 1);
        }

        FileOutputStream fos = new FileOutputStream(fileName);
        fos.write(bytes, 0, index);
        is.get().close();
        fos.close();*/
    }

    protected void deleteOldHtml(String htmlFile) {
        File savedFile = new File(htmlFile);
        if (savedFile.exists()) {
            if (!savedFile.delete()) {
                logger.error("Could not delete saved html file: " + htmlFile);
            }
        }
    }

    @Override
    public void deleteHtmlNews(String id, String basePersistenceFolder) {
        String folder = findSavedFolder(id,basePersistenceFolder);
        File folderFile = new File(folder);
        if (!folderFile.exists()) {
            return;
        }

        for (File file : folderFile.listFiles()) {
            String fileName = file.getName();
            if (fileName.contains(id)) {
                file.delete();
            }
        }
    }

    protected String generateHtmlFileName(String urlString, String basePersistenceFolder) {
        String saveName = StringUtils.findHtmlName(urlString);
        String folder = findSavedFolder(urlString, basePersistenceFolder);

        File folderFile = new File(folder);
        if (!folderFile.exists()) {
            if (folderFile.mkdir()) {
                logger.error("can not make folder: " + folder + " for url: " + urlString);
            }
        }

        return folder + File.separator + saveName;
    }

    protected String findSavedFolder(String urlString ,String basePersistenceFolder) {
        String preFolder = StringUtils.findPreFolder(urlString);

        return basePersistenceFolder + File.separator + preFolder;
    }

  /*  protected void scheduleToSaveIndex() throws Exception {
        URL indexUrl = new URL("http://www.fengsung.com");
        String indexName = "index.html";
        ScheduledExecutorService service = Executors.newScheduledThreadPool(2);

        service.scheduleWithFixedDelay(new ScheduledPageSaver(indexUrl, indexName), 0, 30, TimeUnit.MINUTES);
    }*/

    protected void prepareParameters(String newsTitle, String newsColumnId, StringBuilder basicHql, Map<String, Object> parameters) {
        if (!StringUtils.isNullOrEmpty(newsTitle)) {
            basicHql.append(" and n.title like :indexTitle ");
            parameters.put("indexTitle", "%" + newsTitle + "%");
        }

        if (!StringUtils.isNullOrEmpty(newsColumnId)) {
            basicHql.append(" and i.columnId=:newsColumnId ");
            parameters.put("newsColumnId", newsColumnId);
        }

        basicHql.append(" order by n.publishDate desc");
    }

    protected void prepareParametersWithIndexTitle(String newsTitle, String newsIndexTitle, String newsColumnId, StringBuilder basicHql, Map<String, Object> parameters) {
        if (!StringUtils.isNullOrEmpty(newsTitle)) {
            basicHql.append(" and n.title like :newsTitle ");
            parameters.put("newsTitle", "%" + newsTitle.trim() + "%");
        }

        if (!StringUtils.isNullOrEmpty(newsIndexTitle)) {
            basicHql.append(" and n.indexTitle like :newsIndexTitle ");
            parameters.put("newsIndexTitle", "%" + newsIndexTitle.trim() + "%");
        }

        if (!StringUtils.isNullOrEmpty(newsColumnId)) {
            basicHql.append(" and i.columnId=:newsColumnId ");
            parameters.put("newsColumnId", newsColumnId);
        }
    }


    protected void sortAboutNewsListByIndexLevel(List<News> allAboutNewsList) {
        for (int i = 0; i < allAboutNewsList.size() - 1; i++) {
            for (int j = 0; j < allAboutNewsList.size() - 1 - i; j++) {
                News firstNews = allAboutNewsList.get(j);
                News secondNews = allAboutNewsList.get(j + 1);
                if (!firstNewsMoreImportant(firstNews, secondNews)) {
                    allAboutNewsList.set(j, secondNews);
                    allAboutNewsList.set(j + 1, firstNews);
                }
            }
        }
    }

    protected boolean firstNewsMoreImportant(News firstNews, News secondNews) {
        if (firstNews.getCreateDate().getTime() + 86400000 * 15 < secondNews.getCreateDate().getTime()) {
            return false;
        }

        if (firstNews.getInIndex() > secondNews.getInIndex()) {
            return true;
        }

        if (firstNews.getInIndex() == secondNews.getInIndex()) {
            if (firstNews.getIndexLevel() > secondNews.getIndexLevel()) {
                return true;
            }
        }

        return false;
    }

    protected List<News> searchAboutNewsByKeywords(String rawKeyword) {
        /*StringBuilder basicHql = new StringBuilder("select new News(n.id, n.title, n.keywords, n.createDate, n.indexLevel,n.inIndex) "
                + "from News n where n.keywords like :rawKeyword and n.id not in (:idList) order by n.createDate desc");*/
        StringBuilder basicHql = new StringBuilder("select new News(n.id, n.title, n.keywords, n.createDate, n.indexLevel,n.inIndex) "
                + "from News n where n.keywords like :rawKeyword  order by n.publishDate desc");

        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("rawKeyword", rawKeyword + "%");

        return newsDao.findItemsWithPage(basicHql.toString(), parameters, 1, 9);
    }

    protected List<News> searchNewsTemplate(NewsTemplate newsTemplate, int inIndex) {
        StringBuilder basicHql = new StringBuilder("select new News(n.id,n.title,n.indexTitle,n.keywords,n.createDate,n.briefImage,n.briefImageTitle,n.introduction,n.briefImageVertical) "
                + "from News n, NewsColumnItem i, NewsColumn c where i.newsId=n.id and i.columnId=c.id and "
                + " n.indexLevel=:indexLevel and n.inIndex>=:inIndex and n.published=:published order by n.createDate desc");

        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("indexLevel", newsTemplate.getLevel());
        parameters.put("inIndex", inIndex);
        parameters.put("published", true);

        return newsDao.findItemsWithPage(basicHql.toString(), parameters, 1, newsTemplate.getCount());
    }

    protected List<News> searchNewsByColumnAndTemplate(List<String> newsColumnNameList, NewsTemplate newsTemplate, int inIndex) {
        StringBuilder basicHql = new StringBuilder("select new News(n.id,n.title,n.indexTitle,n.keywords,n.createDate,n.briefImage,n.briefImageTitle,n.introduction,n.briefImageVertical) "
                + "from News n, NewsColumnItem i,NewsColumn c where i.newsId=n.id and i.columnId=c.id and "
                + "c.name in (:columnNameList) and n.indexLevel=:indexLevel and n.inIndex>=:inIndex order by n.createDate desc");

        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("columnNameList", newsColumnNameList);
        parameters.put("indexLevel", newsTemplate.getLevel());
        parameters.put("inIndex", inIndex);

        return newsDao.findItemsWithPage(basicHql.toString(), parameters, 1, newsTemplate.getCount());
    }

    protected List<News> searchNewsByColumnAndTemplate(String newsColumnName, NewsTemplate newsTemplate, int inIndex) {
        StringBuilder basicHql = new StringBuilder("select new News(n.id,n.title,n.indexTitle,n.keywords,n.createDate,n.briefImage,n.briefImageTitle,n.introduction,n.briefImageVertical) "
                + "from News n, NewsColumnItem i, NewsColumn c where i.newsId=n.id and i.columnId=c.id and "
                + "c.name=:columnName and n.indexLevel=:indexLevel and n.inIndex>=:inIndex order by n.createDate desc");

        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("columnName", newsColumnName);
        parameters.put("indexLevel", newsTemplate.getLevel());
        parameters.put("inIndex", inIndex);

        return newsDao.findItemsWithPage(basicHql.toString(), parameters, 1, newsTemplate.getCount());
    }

    protected void saveIndex() {

    }

    //Truncate title and introduction if necessary
    protected void formatNews(News news, int titleLength, int introductionLength) {
        news.setFullTitle(news.getTitle());
        news.setTitle(StringUtils.truncateString(news.getTitle(), titleLength));
        news.setIntroduction(StringUtils.truncateString(news.getIntroduction(), introductionLength));
    }

    protected void formatNewsListByIndexLevel(List<News> newsList, int level) {
        int titleLength = determineTitleLengthByLevel(level);
        int introductionLength = determineIntroductionLengthByLevel(level);

        for (News news : newsList) {
            formatNews(news, titleLength, introductionLength);
        }
    }

    protected int determineIntroductionLengthByLevel(int level) {
        switch (level) {
            case 1:
                return 48;
            case 2:
                return 48;
            case 3:
                return 48;
            case 4:
                return 48;
        }

        return 20;
    }

    protected int determineTitleLengthByLevel(int level) {
        switch (level) {
            case 1:
                return 12;
            case 2:
                return 16;
            case 3:
                return 13;
            case 4:
                return 20;
        }

        return 50;
    }

    protected News createMockNews(String title) {
        News news0 = new News();
        news0.setId(StringUtils.createGuid());
        news0.setContext("just test");
        news0.setKeywords("k1,k2");
        news0.setTitle(title);
        news0.setPublishDate(new Date());
        news0.setCreateDate(new Date());

        return news0;
    }

    @Resource(name = "newsDao")
    public void setNewsDao(NewsDao newsDao) {
        this.newsDao = newsDao;
    }

    @Resource(name = "newsColumnItemDao")
    public void setNewsColumnItemDao(NewsColumnItemDao newsColumnItemDao) {
        this.newsColumnItemDao = newsColumnItemDao;
    }

    @Resource(name = "newsTemplateSorter")
    public void setNewsTemplateSorter(NewsTemplateSorter newsTemplateSorter) {
        this.newsTemplateSorter = newsTemplateSorter;
    }
}
